# _*_ coding: utf-8 _*_
#!/usr/bin/env python

#      ___________________
#  ___| Modulbeschreibung \_________________________________________________________________________
# | Ist für das Laden und halten der Mapdaten und das Bereitstellen von Funktionen um daraus zu
# | lesen.
# |   > Laden der XML-Datei, welche die Mapdaten enthält
# |   > Laden des Tilesets
# |   > Erzeugen der RAW-Dateien für das Tilest um schnelleres Rendern zu ermöglichen
# |
# |__________________________________________________________________| Zuständigkeit [Hendrik] |____

import sys, os
import pickle
import pygame
import xml.sax
if sys.platform == 'win32':
	# Aus Windows-Build importieren
	path = 'lib/NumPtr/build/lib.win32-2.5'
else:
	# Aus Linux-Build importieren
	path = 'lib/NumPtr/build/lib.linux-i686-2.5'
sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), path))
import NumPtr
import Numeric

def addWorkPath(filename):
	"""Aus Portabilitätsgruenden wie relativen Pfadangaben unter verschiedenen Systemen gehandhabt werden. Haengt das Arbeitsverzeichnis vor den übergebenen Dateinamen"""
	return os.path.join(os.path.dirname(sys.argv[0]), filename)

class Map(object):
	def __init__(self, mappath = ''):
		"""Konstruktor"""
		self.tileset			= None
		self.tileset_pointer	= None
		self.tile_size			= (8, 8)
		self.tileset_size		= (0, 0)
		self.tilemap			= []
		self.startpositions		= []
		self.checkpoints		= []
		self.tilemap_pointer	= None
		self.tilemap_size		= (0, 0)
		self.map_pixel_size		= (0, 0)
		
		self.minimap			= None
		self.loadMap(mappath)
	
	def loadRawFiles(self, path):
		"""Versucht die Rawdatei zum übergebenen Tilesetpfad zu laden. Existiert diese nicht, wird sie erstellt"""
		directory, filename			= os.path.split(path)
		raw_path					= os.path.join(directory, filename + '.raw')
		# Wenn die Datei nicht schon existiert, wird sie erzeugt, sonst geladen
		# Wenn das ausführende System Windows ist, muss aufgrund eines Bugs die Datei momentan immer neu erzeugt werden!
		if os.path.exists(raw_path) and sys.platform != 'win32':
			print 'Loading raw-files: [%s]...'% raw_path,
			self.raw_tileset		= pygame.image.load(open(raw_path, 'r'), 'raw_image.tga').convert(32)
		else:
			print 'Create raw-files...',
			raw_surf	= self.tileset.copy()
			raw_surf.fill((0, 0, 0))
			tileset_array	= pygame.surfarray.pixels2d(self.tileset)
			raw_array	= pygame.surfarray.pixels2d(raw_surf)
			offset_1d	= 0
			# Die Tilepixel werden sequenziell hintereinander in der Datei abgespeichert
			for tile_y in range(self.tileset.get_height() / self.tile_size[0]):
				for tile_x in range(self.tileset.get_width() / self.tile_size[1]):
					for pixel_y in range(self.tile_size[0]):
						for pixel_x in range(self.tile_size[1]):
							pixel = tileset_array[tile_x * self.tile_size[0] + pixel_x][tile_y * self.tile_size[1] + pixel_y]
							raw_array[offset_1d % self.tileset.get_width()][offset_1d / self.tileset.get_height()] = pixel
							offset_1d += 1
			del raw_array
			pygame.image.save(raw_surf, raw_path)
			self.raw_tileset		= raw_surf
		# Pointer auf die Datei erzeugen (braucht Mode7.c)
		self.raw_tileset_pointer	= NumPtr.getpointer(pygame.surfarray.pixels2d(self.raw_tileset))
		print 'Done'
	
	def loadMap(self, path):
		"""Lädt die Mapdatei und erzeugt die Minimap dynamisch"""
		tileset_path			= addWorkPath('data/maps/Tileset_Mario_Circuit')
		self.tileset			= pygame.image.load(tileset_path + '.png').convert(32)
		self.tileset_pointer	= NumPtr.getpointer(pygame.surfarray.pixels2d(self.tileset))
		self.tileset_size		= (self.tileset.get_width() / self.tile_size[0], self.tileset.get_height() / self.tile_size[1])
		self.loadRawFiles(tileset_path)
		print 'Loading XML-Mapdata...',
		xml.sax.parse(addWorkPath('data/maps/Mario_Circuit_1.map.xml'), XMLHander(self))
		print 'Done'
		self.tilemap			= Numeric.array(self.tilemap, Numeric.Int32)
		self.tilemap_pointer	= NumPtr.getpointer(self.tilemap)
		self.tilemap_size		= (len(self.tilemap[0]), len(self.tilemap))
		self.map_pixel_size		= (self.tilemap_size[0] * self.tile_size[0], self.tilemap_size[1] * self.tile_size[1])
		
		# Minimap erzeugen
		self.minimap			= pygame.Surface((self.tilemap_size[0], self.tilemap_size[1]))
		minimap_array			= pygame.surfarray.pixels2d(self.minimap)
		# Jedes Tile der Map durchlaufen...
		for y in range(self.tilemap_size[1]):
			for x in range(self.tilemap_size[0]):
				tile			= self.tilemap[y][x]
				# Und je nach Position in dem Tileset ein farbiges Pixel setzen
				if tile == 273:
					color = color = 0x0000FF
				elif tile % self.tileset_size[0] < self.tileset_size[0] / 2 and tile < (self.tileset_size[1] / 2) * self.tileset_size[0] or tile > 1335:
					color = 0xD0D0FF
				else:
					color = 0xFF00FF
				minimap_array[x][y] = color
		
		# Colerkey der Map setzten, damit die Räder später transparent werden
		self.minimap.set_colorkey((255, 0, 255))
		# Alphawert der Map, zeichnet diese später durchscheinend
		self.minimap.set_alpha(180)


class XMLHander(xml.sax.ContentHandler):
	"""XML-Parser um die Mapdaten einzulesen"""
	def __init__(self, gamemap):
		"""Konstruktor"""
		self.gamemap		= gamemap
		self.active_tag		= None
	
	def startDocument(self):
		"""Wird beim Start des Dokuments aufgerufen"""
		self.active_tag		= None

	def endDocument(self):
		"""Wird beim Ende des Dokuments aufgerufen"""
		pass

	def startElement(self, name, attrs):
		"""Wird beim Start eines Tags aufgerufen"""
		if name == 'position':
			self.gamemap.startpositions.append((int(attrs['xpos']), int(attrs['ypos'])))
		elif name == 'point':
			self.gamemap.checkpoints.append(((int(attrs['xpos1']), int(attrs['ypos1'])), (int(attrs['xpos2']), int(attrs['ypos2']))))
		elif name == 'tilerow':
			self.gamemap.tilemap.append([])
			self.active_tag	= 'tilerow'
		elif name == 'tile':
			self.active_tag	= 'tile'
			
	def endElement(self, name):
		"""Wird beim Ende eines Tags aufgerufen"""
		self.active_tag	= None

	def characters(self, data):
		"""Wird beim Einlesen von Testdaten zwischen zwei Tags eingelesen"""
		if self.active_tag == 'tile':
			self.gamemap.tilemap[-1].append(int(data))
